package top.hkyzf.util.movement;

/**
 * description: 计算在各种方块上面行走一格消耗的时间
 * 这些成本大致按照 ticks 来测量
 * @author zhufeng
 * @date 2022/8/16 17:59
 * @version V1.0
 */
public interface ActionCosts {
    /**
     * 世界高度
     */
    int WORLD_HEIGHT = 256;

    /**
     * 方块上
     * 4.633
     */
    double WALK_ONE_BLOCK_COST = 20 / 4.317;
    /**
     * 水中
     * 9.091
     */
    double WALK_ONE_IN_WATER_COST = 20 / 2.2;
    /**
     * 灵魂沙上
     * 0.4 但实际上是一半
     */
    double WALK_ONE_OVER_SOUL_SAND_COST = WALK_ONE_BLOCK_COST * 2;
    /**
     * 上梯子
     * 8.511
     */
    double LADDER_UP_ONE_COST = 20 / 2.35;
    /**
     * 下梯子
     * 6.667
     */
    double LADDER_DOWN_ONE_COST = 20 / 3.0;
    /**
     * 潜行
     * 15.385
     */
    double SNEAK_ONE_BLOCK_COST = 20 / 1.3;
    /**
     * 冲刺
     * 3.564
     */
    double SPRINT_ONE_BLOCK_COST = 20 / 5.612;
    /**
     * 冲刺系数
     * 0.769
     */
    double SPRINT_MULTIPLIER = SPRINT_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST;
    /**
     * 走下一个方块所需要的时间
     * 走出方块需要 0.5 走到边缘，然后需要 0.3 开始下落（落下的同时也在向前移动）
     * 3.706
     */
    double WALK_OFF_BLOCK_COST = WALK_ONE_BLOCK_COST * 0.8;
    /**
     * 下落完毕后走到方块中间所需的时间
     * 0.927
     */
    double CENTER_AFTER_FALL_COST = WALK_ONE_BLOCK_COST - WALK_OFF_BLOCK_COST;

    /**
     * 花费的最大值 Infinity
     * 不设置为 Double.MAX_VALUE 是因为还要加其他东西，有可能会溢出成为负数
     */
    double COST_INF = 1000000;
    /**
     * 下落 1.25 个方块花费时间
     */
    double FALL_1_25_BLOCKS_COST = fallDistanceCostTicks(1.25);
    /**
     * 下落 0.25 个方块花费时间
     */
    double FALL_0_25_BLOCKS_COST = fallDistanceCostTicks(0.25);
    /**
     * 向上跳一格花费时间
     * 当你按空格时，会向上跳1.25个高度，然后向下掉落0.25个高度，落在高一格的方块上
     * 由于抛物线是对称的，所以从1->1.25花费的时间与1.25->1花费的时间相同
     * 同样的如果下落1.25个高度需要花费 X ticks ，那么上升1.25个高度一样需要 X ticks
     * 因此，从 0->1.25 需要时间是 distanceToTicks(1.25) ，从 1.25->1 需要 distanceToTicks(0.25)
     * 因此，从 y=0 到 y=1 需要的时间是 distanceToTicks(1.25)-distanceToTicks(0.25) ticks。
     */
    double JUMP_ONE_BLOCK_COST = FALL_1_25_BLOCKS_COST - FALL_0_25_BLOCKS_COST;
    /**
     * 生成下落 N 个方块所花费时间的数组
     */
    double[] FALL_N_BLOCKS_COST = generateFallBlocksCostArray();

    /**
     * 生成下落 N 个方块所花费时间的数组
     * @return 花费的时间
     */
    static double[] generateFallBlocksCostArray() {
        double[] costs = new double[WORLD_HEIGHT + 1];
        for (int i = 0; i < WORLD_HEIGHT+1; i++) {
            costs[i] = fallDistanceCostTicks(i);
        }
        return costs;
    }

    /**
     * 计算下落 distance 格所需的时间
     * @param distance 距离
     * @return 花费的时间
     */
    static double fallDistanceCostTicks(double distance) {
        // 避免 0/0 NaN
        if (distance == 0) {
            return 0;
        }
        double tmpDistance = distance;
        int tickCount = 0;
        while (true) {
            double fallDistance = velocity(tickCount);
            if (tmpDistance <= fallDistance) {
                return tickCount + tmpDistance / fallDistance;
            }
            tmpDistance -= fallDistance;
            tickCount++;
        }
    }

    /**
     * 下落的速度
     * @param ticks 时间
     * @return 传入 ticks 时间内下落的距离
     */
    static double velocity(int ticks) {
        return (Math.pow(0.98, ticks) - 1) * -3.92;
    }

    /**
     * 速度的旧公式
     * @param ticks 时间
     * @return 传入 ticks 时间内下落的距离
     */
    @Deprecated
    static double oldVelocity(double ticks) {
        return -3.92 * (99 - 49.5 * (Math.pow(0.98, ticks) + 1) - ticks);
    }
}
